package com.wq.base.core.web.filter.log;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.resource.ResourceUrlProvider;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.Charset;

/**
 * 日志过滤器，打印请求参数和响应数据
 * @ClassName:    LogFilter
 * @version:      1.0.0
 * @author:       wqc
 * @date:         2022/4/11 17:38
 */

@Slf4j
@Configuration
public class LogFilter extends OncePerRequestFilter {

    @Resource
    private ResourceUrlProvider resourceUrlProvider;

    private static final String REQUEST_PREFIX_NAME = "Api请求:【URI:";
    private static final String RESPONSE_PREFIX_NAME = "Api响应:【URI:";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        request = new RequestWrapper(request);
        response = new ResponseWrapper(response);

        boolean isStaticResource = isStaticResource(request.getRequestURI());

        printRequestLog(request, isStaticResource);
        Long startTime = System.currentTimeMillis();
        //Spring通过DispatchServlet处理请求
        filterChain.doFilter(request, response);
        Long endTime = System.currentTimeMillis();
        printResponseLog(request, (ResponseWrapper) response, endTime-startTime, isStaticResource);
    }

    private void printRequestLog(final HttpServletRequest request, boolean isStaticResource) {
        if (isStaticResource) {
            return;
        }

        StringBuilder msg = new StringBuilder();
        msg.append(REQUEST_PREFIX_NAME);
        msg.append(request.getRequestURI());
        HttpSession session = request.getSession(false);
        if (session != null) {
            msg.append("; sessionId:").append(session.getId());
        }
        if (request.getMethod() != null) {
            msg.append("; method:").append(request.getMethod());
        }
        if (request.getContentType() != null) {
            msg.append("; contentType:").append(request.getContentType());
        }
        if (request.getQueryString() != null) {
            msg.append("; queryParams:").append(request.getQueryString());;
        }

        if (request instanceof RequestWrapper && !isMultipart(request) && !isBinaryContent(request)) {
            RequestWrapper requestWrapper = (RequestWrapper) request;
            String requestBodyStr = requestWrapper.getRequestBodyString(request);
            if (StringUtils.hasText(requestBodyStr))
                msg.append("payload:").append(requestBodyStr);
        }
        log.info(msg.toString()+"】");
    }

    private boolean isBinaryContent(final HttpServletRequest request) {
        if (request.getContentType() == null) {
            return false;
        }
        return request.getContentType().startsWith("image")
                || request.getContentType().startsWith("video")
                || request.getContentType().startsWith("audio");
    }

    private boolean isMultipart(final HttpServletRequest request) {
        return request.getContentType() != null
                && request.getContentType().startsWith("multipart/form-data");
    }

    private void printResponseLog(HttpServletRequest request, final ResponseWrapper response, long duration, boolean isStaticResource) {
        if (isStaticResource) {
            return;
        }

        StringBuilder msg = new StringBuilder();
        msg.append(RESPONSE_PREFIX_NAME);
        msg.append(request.getRequestURI());
        msg.append("; payload:").append(new String(response.toByteArray(), Charset.defaultCharset()))
                .append(";耗时:").append(duration).append("(ms)");

        log.info(msg.toString()+"】");
    }

    private boolean isStaticResource(String uri) {
        if (uri.contains(".")) {
            // 对于restful风格, 可以认为uri中包含.的为静态资源
            return true;
        }
        String staticUri = resourceUrlProvider.getForLookupPath(uri);
        return staticUri != null;
    }
}

